package com.atguigu.gmall.order.biz.impl;
import java.util.Date;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.atguigu.gmall.cart.entity.CartInfo;
import com.atguigu.gmall.common.config.mq.MqService;
import com.atguigu.gmall.common.constant.MqConst;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.exception.GmallException;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.common.util.UserAuthUtil;
import com.atguigu.gmall.enums.OrderStatus;
import com.atguigu.gmall.enums.PaymentWay;
import com.atguigu.gmall.enums.ProcessStatus;
import com.atguigu.gmall.feign.cart.CartFeignClient;
import com.atguigu.gmall.feign.product.ProductSkuDetailsFeignClient;
import com.atguigu.gmall.feign.user.UserFeignClient;
import com.atguigu.gmall.feign.ware.WareFeignClient;
import com.atguigu.gmall.mq.logistic.OrderLogisticMsg;
import com.atguigu.gmall.mq.ware.WareStockResultMsg;
import com.atguigu.gmall.order.biz.OrderBizService;
import com.atguigu.gmall.order.entity.OrderDetail;
import com.atguigu.gmall.order.entity.OrderInfo;
import com.atguigu.gmall.order.service.OrderDetailService;
import com.atguigu.gmall.order.service.OrderInfoService;
import com.atguigu.gmall.order.vo.OrderConfirmRespVo;
import com.atguigu.gmall.order.vo.OrderSplitReps;
import com.atguigu.gmall.order.vo.OrderSubmitVo;
import com.atguigu.gmall.order.vo.SkuWare;
import com.atguigu.gmall.user.entity.UserAddress;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.swing.plaf.basic.BasicTreeUI;
import java.math.BigDecimal;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * @author tangsy
 * @Description TODO
 * @date 2022/12/21 20:22
 * @Version 1.0
 */
@Slf4j
@Service
public class OrderBizServiceImpl implements OrderBizService {

    @Autowired
    CartFeignClient cartFeignClient;

    @Autowired
    ProductSkuDetailsFeignClient skuDetailFeignClient;

    @Autowired
    UserFeignClient userFeignClient;

    @Autowired
    WareFeignClient wareFeignClient;

    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    OrderInfoService orderInfoService;

    @Autowired
    OrderDetailService orderDetailService;

    @Autowired
    MqService mqService;

    @Override
    public OrderConfirmRespVo getConfirmData() {
        OrderConfirmRespVo vo = new OrderConfirmRespVo();

        //1、商品列表： 远程找购物车要到所有选中的商品
        List<CartInfo> data = cartFeignClient.getChecked().getData();
        //【小心丢失请求头风险，一定要开启feign拦截器】
        List<OrderConfirmRespVo.SkuDetail> collect = data.stream()
                .map(item -> {
                    OrderConfirmRespVo.SkuDetail detail = new OrderConfirmRespVo.SkuDetail();
                    detail.setSkuId(item.getSkuId());
                    detail.setImgUrl(item.getImgUrl());
                    detail.setSkuName(item.getSkuName());
                    detail.setSkuNum(item.getSkuNum());
                    //商品的实时价格
                    BigDecimal price = skuDetailFeignClient.getSkuInfo(item.getSkuId()).getData().getPrice();
                    detail.setOrderPrice(price);
                    //查询这个商品的库存状态
                    String hasStock = wareFeignClient.hasStock(item.getSkuId(), item.getSkuNum());
                    detail.setHasStock(hasStock);
                    return detail;
                }).collect(Collectors.toList());
        vo.setDetailArrayList(collect);

        //2、总数量
        Integer totalNum = collect.stream()
                .map(OrderConfirmRespVo.SkuDetail::getSkuNum)
                .reduce((o1, o2) -> o1 + o2)
                .get();
        vo.setTotalNum(totalNum);

        //3、总金额
        BigDecimal totalAmount = collect.stream()
                .map(item -> item.getOrderPrice().multiply(new BigDecimal(item.getSkuNum())))
                .reduce((o1, o2) -> o1.add(o2))
                .get();
        vo.setTotalAmount(totalAmount);

        //4、收货地址列表
        Long userId = UserAuthUtil.getUserId();
        List<UserAddress> addresses = userFeignClient.getUserAddress(userId).getData();
        vo.setUserAddressList(addresses);

        //5、流水号
        //1）、开启整个订单的追踪功能
        //2）、防止订单重复提交
        String tradeNo = "ATGUIGU-"+System.currentTimeMillis()+"-"+userId;
        //给客户端放一个流水号
        vo.setTradeNo(tradeNo);
        //给redis放一个流水号
        redisTemplate.opsForValue().set(RedisConst.REPEAT_TOKEN+tradeNo,"1",5, TimeUnit.MINUTES);

        return vo;
    }

    @Transactional
    @Override
    public Long submitOrder(OrderSubmitVo submitVo, String tradeNo) {

        //1、校验令牌
        Boolean delete = redisTemplate.delete(RedisConst.REPEAT_TOKEN + tradeNo);
        if(!delete){
            throw new GmallException(ResultCodeEnum.REPEAT_REQUEST);
        }

        //2、校验库存
        List<OrderSubmitVo.OrderDetailListDTO> noStockSku = submitVo.getOrderDetailList()
                .stream()
                .filter(item -> "0".equals(wareFeignClient.hasStock(item.getSkuId(), item.getSkuNum())))
                .collect(Collectors.toList());
        if(noStockSku!=null && noStockSku.size()>0){
            String skuNames = noStockSku.stream()
                    .map(OrderSubmitVo.OrderDetailListDTO::getSkuName)
                    .reduce((o1, o2) -> o1 + "；" + o2)
                    .get();
            GmallException exception =
                    new GmallException(skuNames + "； 没有库存",ResultCodeEnum.NO_STOCK.getCode());
            throw  exception;
        }

        //3、校验价格
        List<OrderSubmitVo.OrderDetailListDTO> priceChangeSkus = submitVo.getOrderDetailList()
                .stream()
                .filter(item -> {
                    BigDecimal orderPrice = item.getOrderPrice();
                    BigDecimal price = skuDetailFeignClient.getSkuInfo(item.getSkuId()).getData().getPrice();
                    return Math.abs(orderPrice.subtract(price).doubleValue()) >= 0.0001;
                }).collect(Collectors.toList());

        if(priceChangeSkus!=null && priceChangeSkus.size()>0){
            String skuNames = priceChangeSkus.stream()
                    .map(OrderSubmitVo.OrderDetailListDTO::getSkuName)
                    .reduce((o1, o2) -> o1 + "；" + o2)
                    .get();
            GmallException exception =
                    new GmallException(skuNames + "； 价格变化，请刷新页面重新确认",
                            ResultCodeEnum.PRICE_CHANGE.getCode());
            throw  exception;
        }


        //1、给order_info保存订单基本信息
        OrderInfo orderInfo = prepareOrderInfo(submitVo,tradeNo);
        orderInfoService.save(orderInfo);
        //订单用雪花算法生成的id
        Long orderId = orderInfo.getId();

        //30min以后要关闭
//        ScheduledExecutorService service = Executors.newScheduledThreadPool(4);
//        service.schedule(()->{
//            closeOrder(orderInfo);
//        },30,TimeUnit.MINUTES);


        //2、给 order_detail 保存订单明细信息
        List<OrderDetail> orderDetails = prepareOrderDetails(submitVo,orderInfo);
        orderDetailService.saveBatch(orderDetails);

        //3、发送订单创建成功消息
        mqService.send(orderInfo, MqConst.ORDER_EVENT_EXCHANGE,MqConst.ORDER_CREATE_RK);


        //4、删除购物车中选中的商品
        cartFeignClient.deleteChecked();
        return orderId;
    }

    @Override
    public void closeOrder(Long id, Long userId) {

        ProcessStatus closed = ProcessStatus.CLOSED;
        //只有订单未支付的情况下才需要关闭；
        //process_status=CLOSED
        //order_status=CLOSED
        //能来关单都是超了30min的订单消息； 是幂等的
        boolean update = orderInfoService.lambdaUpdate()
                .set(OrderInfo::getOrderStatus, closed.getOrderStatus().name())
                .set(OrderInfo::getProcessStatus, closed.name())
                .eq(OrderInfo::getId, id)
                .eq(OrderInfo::getUserId, userId)
                .eq(OrderInfo::getOrderStatus, OrderStatus.UNPAID.name()) //这两个eq保证关单的幂等
                .eq(OrderInfo::getProcessStatus, ProcessStatus.UNPAID.name())//这两个eq保证关单的幂等
                .update();
        log.info("订单：{},关闭：{}",id,update);
    }

    @Override
    public void payedOrder(String out_trade_no, Long userId) {
        ProcessStatus payed = ProcessStatus.PAID;
        //修改订单为已支付状态
        boolean update = orderInfoService.lambdaUpdate()
                .set(OrderInfo::getOrderStatus, payed.getOrderStatus().name())
                .set(OrderInfo::getProcessStatus, payed.name())
                .eq(OrderInfo::getUserId, userId)
                .eq(OrderInfo::getOutTradeNo, out_trade_no)
                .in(OrderInfo::getOrderStatus, OrderStatus.UNPAID.name(), OrderStatus.CLOSED.name())
                .in(OrderInfo::getProcessStatus, ProcessStatus.UNPAID.name(), ProcessStatus.CLOSED.name())
                .update();
        log.info("修改{}订单，已支付状态：{}",out_trade_no,update);
    }

    @Override
    public void updateOrderStockStatus(WareStockResultMsg result) {
        //1、最终订单要修改成的状态
        ProcessStatus status = ProcessStatus.WAITING_DELEVER;
        switch (result.getStatus()) {
            case "DEDUCTED": status = ProcessStatus.WAITING_DELEVER; break; //扣减就是等待发货
            case "OUT_OF_STOCK": status = ProcessStatus.STOCK_EXCEPTION; break; //扣减失败就是等待调货
        }

        OrderInfo orderInfo = orderInfoService.getById(result.getOrderId());

        //注意：一旦使用消息队列，就和http没有任何关系了，我们以前透传的所有东西都不能用；
        //如果想要后来用的字段，发消息的时候就必须带上。
        //2、修改订单状态
        orderInfoService.lambdaUpdate()
                .set(OrderInfo::getOrderStatus,status.getOrderStatus().name())
                .set(OrderInfo::getProcessStatus,status.name())
                .eq(OrderInfo::getId,orderInfo.getId())
                .eq(OrderInfo::getUserId,orderInfo.getUserId())
                .eq(OrderInfo::getOrderStatus,OrderStatus.PAID.name())
                .eq(OrderInfo::getProcessStatus,ProcessStatus.PAID.name())
                .update();
        log.info("订单库存状态更新完成");

        //下电子面单，进行发货
        if("DEDUCTED".equals(result.getStatus())){
            OrderLogisticMsg msg = new OrderLogisticMsg();
            msg.setOrderId(orderInfo.getId());
            msg.setUserId(orderInfo.getUserId());
            //给等待物流配送的订单队列发送消息
            mqService.send(msg,MqConst.ORDER_EVENT_EXCHANGE,MqConst.ORDER_LOGISTIC_RK);
        }

    }

    @Override
    public List<OrderSplitReps> orderSplit(Long orderId, String json) {
        //大订单（orderId）拆分成  子订单（根据大订单中所有商品的库存分布，拆分成子订单，把子订单都存到数据库）
        OrderInfo parentOrder = orderInfoService.getById(orderId);

        //拿到大订单中所有商品
        List<OrderDetail> orderDetails = orderDetailService.lambdaQuery()
                .eq(OrderDetail::getOrderId, parentOrder.getId())
                .eq(OrderDetail::getUserId, parentOrder.getUserId())
                .list();

        //1、得到大订单中所有商品的库存分布
        List<SkuWare> skuWares = JSON.parseObject(json, new TypeReference<List<SkuWare>>() {
        });

        AtomicInteger i = new AtomicInteger(0);
        //2、拆分子订单
        List<OrderInfo> childOrders = skuWares.stream()
                .map(item->{
                    OrderInfo childOrder = new OrderInfo();

                    childOrder.setConsignee(parentOrder.getConsignee());
                    childOrder.setConsigneeTel(parentOrder.getConsigneeTel());
                    //子订单总额：子订单负责的商品的总额
                    List<Long> skuIds = item.getSkuIds(); //当前子订单负责的所有商品id
                    //拿到子订单负责的所有商品
                    List<OrderDetail> childDetails = orderDetails.stream()
                            .filter(obj -> skuIds.contains(obj.getSkuId()))
                            .collect(Collectors.toList());
                    childOrder.setOrderDetails(childDetails);

                    //计算子订单总额
                    BigDecimal totalAmount = childDetails.stream()
                            .map(o1 -> o1.getOrderPrice().multiply(new BigDecimal(o1.getSkuNum())))
                            .reduce((o1, o2) -> o1.add(o2))
                            .get();
                    childOrder.setTotalAmount(totalAmount);

                    childOrder.setOrderStatus(parentOrder.getOrderStatus());
                    childOrder.setUserId(parentOrder.getUserId());
                    childOrder.setPaymentWay(parentOrder.getPaymentWay());
                    childOrder.setDeliveryAddress(parentOrder.getDeliveryAddress());
                    childOrder.setOrderComment(parentOrder.getOrderComment());
                    childOrder.setOutTradeNo(i.getAndIncrement()+"_"+parentOrder.getOutTradeNo());

                    childOrder.setTradeBody(childDetails.get(0).getSkuName());
                    childOrder.setCreateTime(new Date());
                    childOrder.setExpireTime(parentOrder.getExpireTime());
                    childOrder.setProcessStatus(parentOrder.getProcessStatus());
                    childOrder.setTrackingNo("");
                    childOrder.setParentOrderId(parentOrder.getId());
                    childOrder.setImgUrl(childDetails.get(0).getImgUrl());
                    childOrder.setOperateTime(new Date());
                    childOrder.setActivityReduceAmount(new BigDecimal("0"));
                    childOrder.setCouponAmount(new BigDecimal("0"));
                    childOrder.setOriginalTotalAmount(totalAmount);
                    childOrder.setFeightFee(new BigDecimal("0"));
                    childOrder.setWareId(item.getWareId());

                    //准备返回数据

                    return childOrder;
                }).collect(Collectors.toList());

        for (OrderInfo orderInfo : childOrders) {
            //保存子订单
            orderInfoService.save(orderInfo);
            Long id = orderInfo.getId();

            //保存子订单的明细
            List<OrderDetail> details = orderInfo.getOrderDetails().stream()
                    .map(item -> {
                        item.setOrderId(id); //回填子订单id
                        return item;
                    }).collect(Collectors.toList());
            orderDetailService.saveBatch(details);
        }

        //把父订单改为已拆分
        boolean update = orderInfoService.lambdaUpdate()
                .set(OrderInfo::getOrderStatus, OrderStatus.SPLIT.name())
                .set(OrderInfo::getProcessStatus, ProcessStatus.SPLIT.name())
                .eq(OrderInfo::getId, parentOrder.getId())
                .eq(OrderInfo::getUserId, parentOrder.getUserId())
                .update();

        List<Long> ids = childOrders.stream().map(item -> item.getId()).collect(Collectors.toList());
        log.info("拆单完成：大订单：{} 拆分为：{}",parentOrder.getId(),ids);

        //准备响应结果
        List<OrderSplitReps> collect = childOrders.stream()
                .map(item -> {
                    OrderSplitReps reps = new OrderSplitReps();
                    reps.setOrderId(item.getId());
                    reps.setUserId(item.getUserId());
                    reps.setConsignee(item.getConsignee());
                    reps.setConsigneeTel(item.getConsigneeTel());
                    reps.setOrderComment(item.getOrderComment());
                    reps.setOrderBody(item.getTradeBody());
                    reps.setDeliveryAddress(item.getDeliveryAddress());
                    reps.setPaymentWay("2");
                    reps.setWareId(item.getWareId());

                    //订单明细
                    List<OrderDetail> details = item.getOrderDetails();

                    //List<Sku> details
                    List<OrderSplitReps.Sku> skuList = details.stream().map(o1 -> {
                        OrderSplitReps.Sku sku = new OrderSplitReps.Sku();
                        sku.setSkuId(o1.getSkuId());
                        sku.setSkuNum(o1.getSkuNum());
                        sku.setSkuName(o1.getSkuName());
                        return sku;
                    }).collect(Collectors.toList());
                    reps.setDetails(skuList);
                    return reps;
                }).collect(Collectors.toList());

        return collect;
    }

    @Override
    public Long saveSeckillOrder(OrderInfo info) {
        //1、保存订单
        boolean save = orderInfoService.save(info);
        Long id = info.getId();
        //2、保存订单明细
        List<OrderDetail> details = info.getOrderDetails()
                .stream()
                .map(item -> {
                    item.setOrderId(id); //回填订单id
                    return item;
                }).collect(Collectors.toList());
        orderDetailService.saveBatch(details);

        //TODO 独立再设计一套MQ队列交换机等
        return id;
    }

    private List<OrderDetail> prepareOrderDetails(OrderSubmitVo submitVo, OrderInfo orderInfo) {
        List<OrderDetail> details = submitVo.getOrderDetailList()
                .stream()
                .map(item -> {
                    OrderDetail orderDetail = new OrderDetail();
                    orderDetail.setOrderId(orderInfo.getId());
                    orderDetail.setUserId(orderInfo.getUserId());
                    orderDetail.setSkuId(item.getSkuId());
                    orderDetail.setSkuName(item.getSkuName());
                    orderDetail.setImgUrl(item.getImgUrl());
                    orderDetail.setOrderPrice(item.getOrderPrice());
                    orderDetail.setSkuNum(item.getSkuNum());
                    orderDetail.setCreateTime(new Date());
                    orderDetail.setSplitTotalAmount(item.getOrderPrice().multiply(new BigDecimal(item.getSkuNum())));

                    orderDetail.setSplitActivityAmount(new BigDecimal("0"));
                    orderDetail.setSplitCouponAmount(new BigDecimal("0"));


                    return orderDetail;
                })
                .collect(Collectors.toList());

        return details;
    }

//    private OrderInfo prepareOrderInfo(OrderSubmitVo submitVo, String tradeNo) {
//        OrderInfo orderInfo = new OrderInfo();
//
//        orderInfo.setConsignee(submitVo.getConsignee());
//        orderInfo.setConsigneeTel(submitVo.getConsigneeTel());
//        orderInfo.setDeliveryAddress(submitVo.getDeliveryAddress());
//        orderInfo.setOrderComment(submitVo.getOrderComment());
//
//        BigDecimal totalAmount = submitVo.getOrderDetailList().stream()
//                .map(item -> item.getOrderPrice().multiply(new BigDecimal(item.getSkuNum())))
//                .reduce((o1, o2) -> o1.add(o2))
//                .get();
//        orderInfo.setTotalAmount(totalAmount);
//
//        orderInfo.setOrderStatus(OrderStatus.UNPAID.name());
//        orderInfo.setUserId(UserAuthUtil.getUserId());
//        orderInfo.setPaymentWay(PaymentWay.ONLINE.name());
//
//        orderInfo.setOutTradeNo(tradeNo);
//
//        String skuName = submitVo.getOrderDetailList().get(0).getSkuName();
//        orderInfo.setTradeBody(skuName);
//        orderInfo.setCreateTime(new Date());
//
//        Date date = new Date(System.currentTimeMillis() + 30 * 60 * 1000);
//        orderInfo.setExpireTime(date);
//
//        orderInfo.setProcessStatus(ProcessStatus.UNPAID.name());
//        orderInfo.setTrackingNo("");
//        orderInfo.setParentOrderId(null);
//
//        String imgUrl = submitVo.getOrderDetailList().get(0).getImgUrl();
//        orderInfo.setImgUrl(imgUrl);
//        orderInfo.setProvinceId(0L);
//        orderInfo.setOperateTime(new Date());
//
//        orderInfo.setActivityReduceAmount(new BigDecimal("0"));
//        orderInfo.setCouponAmount(new BigDecimal("0"));
//
//        orderInfo.setOriginalTotalAmount(totalAmount);
//
//        orderInfo.setFeightFee(new BigDecimal("0"));
//
//        return null;
//    }

    private OrderInfo prepareOrderInfo(OrderSubmitVo submitVo, String tradeNo) {
        OrderInfo orderInfo = new OrderInfo();

        orderInfo.setConsignee(submitVo.getConsignee());
        orderInfo.setConsigneeTel(submitVo.getConsigneeTel());
        orderInfo.setDeliveryAddress(submitVo.getDeliveryAddress());
        orderInfo.setOrderComment(submitVo.getOrderComment());


        //订单总额： = 原价金额 - 优惠金额
        BigDecimal totalAmount = submitVo.getOrderDetailList()
                .stream()
                .map(item -> item.getOrderPrice().multiply(new BigDecimal(item.getSkuNum())))
                .reduce((o1, o2) -> o1.add(o2))
                .get();
        orderInfo.setTotalAmount(totalAmount);

        //订单状态
        orderInfo.setOrderStatus(OrderStatus.UNPAID.name());
        //用户id
        Long userId = UserAuthUtil.getUserId();
        orderInfo.setUserId(userId);
        //支付方式
        orderInfo.setPaymentWay(PaymentWay.ONLINE.name());
        //对外流水号
        orderInfo.setOutTradeNo(tradeNo);
        //交易体
        String skuName = submitVo.getOrderDetailList().get(0).getSkuName();
        orderInfo.setTradeBody(skuName);
        //创建时间
        orderInfo.setCreateTime(new Date());
        //失效时间  30min不支付，订单就要被关闭
        Date date = new Date(System.currentTimeMillis() + 30 * 60 * 1000);
        orderInfo.setExpireTime(date);

        //处理状态
        orderInfo.setProcessStatus( ProcessStatus.UNPAID.name());
        orderInfo.setTrackingNo("");
        orderInfo.setParentOrderId(null);

        String imgUrl = submitVo.getOrderDetailList().get(0).getImgUrl();
        orderInfo.setImgUrl(imgUrl);
        orderInfo.setProvinceId(0L);
        orderInfo.setOperateTime(new Date());

        orderInfo.setActivityReduceAmount(new BigDecimal("0"));
        orderInfo.setCouponAmount(new BigDecimal("0"));

        //原价金额
        orderInfo.setOriginalTotalAmount(totalAmount);

        orderInfo.setFeightFee(new BigDecimal("0"));

        return orderInfo;
    }
}
